package cn.com.cetccst.logmanager;

import android.content.Context;

import androidx.annotation.NonNull;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.core.appender.RollingFileAppender;
import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
import org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy;
import org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy;
import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
import org.apache.logging.log4j.core.appender.rolling.action.Action;
import org.apache.logging.log4j.core.appender.rolling.action.DeleteAction;
import org.apache.logging.log4j.core.appender.rolling.action.Duration;
import org.apache.logging.log4j.core.appender.rolling.action.IfFileName;
import org.apache.logging.log4j.core.appender.rolling.action.IfLastModified;
import org.apache.logging.log4j.core.appender.rolling.action.PathCondition;
import org.apache.logging.log4j.core.config.AppenderRef;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.nio.charset.StandardCharsets;

public final class LogManager {
    private static final String DEFAULT_LOG_PATTERN = "%d %-5level [%t] %msg%xEx%n";
//    private static final String DEFAULT_LOG_PATTERN = "%d %-5level [%t] (%C:%L) %msg%xEx%n";
    private static final String DEFAULT_LOG_NAME = "log.txt";
    private static final Level DEFAULT_LEVEL = Level.TRACE;

    public enum TriggeringModel {MODEL_SIZE_BASED, MODEL_TIME_BASED}

    private LogManager(){}
    public static class Builder {
        //日志文件名
        private String fileName = DEFAULT_LOG_NAME;
        //日志记录模式
        private String pattern = DEFAULT_LOG_PATTERN;
        //日志持久化级别
        private Level level = DEFAULT_LEVEL;
        //日志保存路径
        private final String externalPath;
        //模块名称
        private final String moduleName;
        private TriggeringModel currentModel = TriggeringModel.MODEL_TIME_BASED;

        public Builder(Context context, @NonNull String moduleName) {
            this.moduleName = moduleName;
            externalPath = context.getExternalFilesDir(null) + File.separator + "logs";
        }

        /**
         * 必须以txt文件格式结尾
         *
         * @param fileName 文件名
         * @return
         */
        public Builder setLogFileName(@NonNull String fileName) {
            //TODO 完善内容
            assert (fileName.endsWith(".txt"));
            this.fileName = fileName;
            return this;
        }

        public Builder setRollingModel(TriggeringModel model) {
            this.currentModel = model;
            return this;
        }

        public Builder setLevel(Level level) {
            this.level = level;
            return this;
        }

        public Logger build() {
            //为false时，返回多个LoggerContext对象，true：返回唯一的单例LoggerContext
            final LoggerContext ctx = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
            final Configuration config = ctx.getConfiguration();
            if (config.getAppender(moduleName) != null) {
                return LoggerFactory.getLogger(moduleName);
            }
//                创建一个展示的样式：PatternLayout，还有其他的日志打印样式。
            PatternLayout layout = PatternLayout.newBuilder()
                    .withCharset(StandardCharsets.UTF_8)
                    .withConfiguration(config)
                    .withPattern(pattern)
                    .build();


            Appender appender = null;
            switch (currentModel) {
                case MODEL_SIZE_BASED:
                    appender = generateSizeBasedConfiguration(config, layout);
                    break;
                case MODEL_TIME_BASED:
                    appender = generateTimeBasedConfiguration(config, layout);
                    break;
            }

            Appender consoleAppender = ConsoleAppender.newBuilder()
                    .setConfiguration(config)
                    .withLayout(layout)
                    .setTarget(ConsoleAppender.Target.SYSTEM_OUT)
                    .withName("CONSOLE")
                    .build();
            consoleAppender.start();
            config.addAppender(consoleAppender);
            AppenderRef ref = AppenderRef.createAppenderRef(moduleName, level, null);
            AppenderRef consoleRef = AppenderRef.createAppenderRef("CONSOLE", Level.ALL, null);
            AppenderRef[] refs = new AppenderRef[]{ref, consoleRef};
            LoggerConfig loggerConfig = LoggerConfig.createLogger(false, Level.ALL, moduleName,
                    "true", refs, null, config, null);
            loggerConfig.addAppender(appender, level, null);
            loggerConfig.addAppender(consoleAppender, Level.ALL, null);
            config.addLogger(moduleName, loggerConfig);
            ctx.updateLoggers(config);
            return LoggerFactory.getLogger(moduleName);
        }

        private Appender generateTimeBasedConfiguration(Configuration configuration, PatternLayout layout) {

//            ThresholdFilter thresholdFilter = ThresholdFilter.createFilter(level, Filter.Result.ACCEPT, Filter.Result.DENY);

            final TriggeringPolicy policy = TimeBasedTriggeringPolicy.newBuilder()
                    .withModulate(true)
                    .withInterval(1)
                    .build();

            //删除日志的条件
            IfFileName ifFileName = IfFileName.createNameCondition(fileName.substring(0, fileName.length() - 4) + "-*.txt", null);
            IfLastModified ifLastModified = IfLastModified.createAgeCondition(Duration.parse("7d"));
            DeleteAction deleteAction = DeleteAction.createDeleteAction(
                    externalPath + File.separator + moduleName + File.separator, false, 1, false, null,
                    new PathCondition[]{ifLastModified, ifFileName}, null, configuration);
            Action[] actions = new Action[]{deleteAction};

            DefaultRolloverStrategy strategy = DefaultRolloverStrategy
                    .newBuilder().withCustomActions(actions).withConfig(configuration).build();

            final Appender appender = RollingFileAppender.newBuilder()
                    .withName(moduleName)
                    .withImmediateFlush(true)
                    .withFileName(externalPath + File.separator + moduleName + File.separator + fileName)
                    .withFilePattern(externalPath + File.separator + moduleName + File.separator + fileName.substring(0, fileName.length() - 4) + "-%d{yyyy-MM-dd}.txt")
                    .withLayout(layout)
                    .withPolicy(policy)
                    .withStrategy(strategy)
                    .build();
            appender.start();
            configuration.addAppender(appender);
            return appender;
        }

        private Appender generateSizeBasedConfiguration(Configuration configuration, PatternLayout layout) {

//            ThresholdFilter thresholdFilter = ThresholdFilter.createFilter(level, Filter.Result.ACCEPT, Filter.Result.DENY);

            final TriggeringPolicy policy = SizeBasedTriggeringPolicy.createPolicy("10MB");

            DefaultRolloverStrategy strategy = DefaultRolloverStrategy
                    .newBuilder().withMax("5").withConfig(configuration).build();

            final Appender appender = RollingFileAppender.newBuilder()
                    .withName(moduleName)
                    .withImmediateFlush(true)
                    .withFileName(externalPath + File.separator + moduleName + File.separator + fileName)
                    .withFilePattern(externalPath + File.separator + moduleName + File.separator + fileName.substring(0, fileName.length() - 4) + "-%i.txt")
                    .withLayout(layout)
                    .withStrategy(strategy)
                    .withPolicy(policy)
                    .build();
            appender.start();
            configuration.addAppender(appender);
            return appender;
        }
    }

}
